package com.yuyou.fn.portal.service.impl;

import com.google.common.collect.Lists;
import com.yuyou.fn.common.constant.Constants;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.lang.Tuple2;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.BigDecimals;
import com.yuyou.fn.common.util.TimeUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.entity.AttendClassRecordDetail;
import com.yuyou.fn.educational.entity.CourseClass;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.educational.vo.AttendClassRecordDetailQueryVo;
import com.yuyou.fn.educational.vo.DivRegInfo;
import com.yuyou.fn.portal.entity.CourseDisplayConfig;
import com.yuyou.fn.portal.entity.OuterCourseClass;
import com.yuyou.fn.portal.repository.OuterCourseClassRepository;
import com.yuyou.fn.portal.service.IConfigService;
import com.yuyou.fn.portal.service.IOuterCourseClassService;
import com.yuyou.fn.portal.vo.CourseClassDetail;
import com.yuyou.fn.portal.vo.OuterCourseClassQueryVo;
import com.yuyou.fn.portal.vo.ScheduleInfoVo;
import com.yuyou.fn.portal.vo.SimpleCourseClass;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GeoNearOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * Created by wens on 2017/4/6.
 */
@Service
public class OuterCourseClassServiceImpl implements IOuterCourseClassService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private OuterCourseClassRepository courseClassRepository ;


    @Resource
    private IConfigService configService;

    @Resource
    private IAttendClassRecordDetailService attendClassRecordDetailService;


    private  OuterCourseClass fixStudyingFee(OuterCourseClass courseClass ){
        if(courseClass == null ){
            return null ;
        }
        return courseClass ;
    }

    private  SimpleCourseClass fixStudyingFee(SimpleCourseClass courseClass ){
        if(courseClass == null ){
            return null ;
        }
        return courseClass;
    }

    @Override
    public List<OuterCourseClass> findCourseClassByGrade(String gradeName) {
        return Lists.transform(courseClassRepository.findByGradeName(gradeName),this::fixStudyingFee);
    }


    @Override
    public List<SimpleCourseClass> findOuterCourseClassByCondition(OuterCourseClassQueryVo outerCourseClassQueryVo, int pageNo, int pageSize){
        if(pageNo>0) pageNo=pageNo-1;
        Aggregation aggregation=buildAggregation(outerCourseClassQueryVo,pageNo,pageSize);
        return queryCourse(aggregation);
    }

    @Override
    public Tuple2<List<SimpleCourseClass>,Long> findByConditionForPage(OuterCourseClassQueryVo outerCourseClassQueryVo, int pageNo, int pageSize){

        Query query = buildCourseQuery(outerCourseClassQueryVo);
        long total = mongoTemplate.count(query,OuterCourseClass.class);
        return new Tuple2( queryCourse(pageNo, pageSize, query), total );
    }

    private List<SimpleCourseClass> queryCourse( Aggregation aggregation) {
        AggregationResults<SimpleCourseClass> courseClassResultVoList = mongoTemplate.aggregate(aggregation,"outerCourseClass",SimpleCourseClass.class);

        return Lists.transform(courseClassResultVoList.getMappedResults(),this::fixStudyingFee);
    }

    private List<SimpleCourseClass> queryCourse(int pageNo, int pageSize, Query query) {
        if(pageNo>0) pageNo=pageNo-1;
        query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"isRecommend"))).with(new Sort(new Sort.Order(Sort.Direction.ASC,"progressStatus"),new Sort.Order(Sort.Direction.ASC,"startCourseTime"))).skip(pageNo*pageSize).limit(pageSize);
        List<SimpleCourseClass> courseClassResultVoList = mongoTemplate.find(query,SimpleCourseClass.class);

        return Lists.transform( courseClassResultVoList , this::fixStudyingFee );
    }

    private Criteria buildCriteria(OuterCourseClassQueryVo outerCourseClassQueryVo){
        CourseDisplayConfig courseDisplayConfig = configService.findCourseDisplayConfigFromCache(outerCourseClassQueryVo.getAreaTeamId());
        Criteria criteria=Criteria.where("status").is(1).and("progressStatus").in(Arrays.asList(0,1)); //已开班课程 0:未开课 1:已开课

        if (outerCourseClassQueryVo.getFullClassState()!=null && outerCourseClassQueryVo.getFullClassState()==0){
           criteria.and("fullClassState").is(outerCourseClassQueryVo.getFullClassState());
        }

        if(outerCourseClassQueryVo.getPeriodId() != null ){
            criteria.and("periodId").is(outerCourseClassQueryVo.getPeriodId()) ;
        }else{
            List<Long> periodIds = BeanUtils.toList( courseDisplayConfig.getPeriods(),"periodId" );
            criteria.and("periodId").in(periodIds) ;
        }

        //校区
        if (StringUtils.isNotEmpty(outerCourseClassQueryVo.getCampusIds())){
            criteria.and("campusId").in(Utils.toLongIds(outerCourseClassQueryVo.getCampusIds()));
        }else{
            Set<Long> campusIds = new HashSet<>();
            for(CourseDisplayConfig.Period period : courseDisplayConfig.getPeriods() ){
                if(outerCourseClassQueryVo.getPeriodId() == null || period.getPeriodId().equals(outerCourseClassQueryVo.getPeriodId())){
                    campusIds.addAll(period.getCampusIds());
                }
            }
            criteria.and("campusId").in(campusIds);
        }

        //年级
        if (StringUtils.isNotEmpty( outerCourseClassQueryVo.getGradeIds()) ){
            criteria.and("gradeId").in(Utils.toLongIds(outerCourseClassQueryVo.getGradeIds()));
        }else{
            Set<Long> gradeIds = new HashSet<>();
            for(CourseDisplayConfig.Period period : courseDisplayConfig.getPeriods() ){
                if(outerCourseClassQueryVo.getPeriodId() == null || period.getPeriodId().equals(outerCourseClassQueryVo.getPeriodId())){
                    gradeIds.addAll(period.getGradeIds());
                }
            }
            criteria.and("gradeId").in(gradeIds);
        }



        //科目
        if (StringUtils.isNotEmpty( outerCourseClassQueryVo.getSubjectIds()) ){
            criteria.and("subjectId").in(Utils.toLongIds(outerCourseClassQueryVo.getSubjectIds()));
        }



        //星期
        if (StringUtils.isNotEmpty(  outerCourseClassQueryVo.getTimeIntervals())){
            String timeInterval=outerCourseClassQueryVo.getTimeIntervals();
            String[] timeIntervalList=timeInterval.split(",");
            criteria.and("timeInterval").in(timeIntervalList);
        }




        //老师
        if (outerCourseClassQueryVo.getTeacherId() != null ){
            //query.addCriteria(Criteria.where("teacherId").is(outerCourseClassQueryVo.getTeacherId()));
            criteria.and("teacherId").is(outerCourseClassQueryVo.getTeacherId());
        }

        //班型
        if(StringUtils.isNotEmpty(outerCourseClassQueryVo.getLevels())){
            //query.addCriteria(Criteria.where("level").in(Utils.toLongIds(outerCourseClassQueryVo.getLevels())));
            criteria.and("level").in(Utils.toLongIds(outerCourseClassQueryVo.getLevels()));
        }

        //段次
        if(StringUtils.isNotEmpty(outerCourseClassQueryVo.getSegmentNos())){
            criteria.and("segmentNo").in(Utils.toLongIds(outerCourseClassQueryVo.getSegmentNos()));
        }

        //关键字搜索
        if(StringUtils.isNotEmpty(outerCourseClassQueryVo.getSearchKeyword())){
            criteria.andOperator(new Criteria().orOperator(Criteria.where("className").regex(".*" +outerCourseClassQueryVo.getSearchKeyword()+ ".*") ,Criteria.where("campusName").regex(".*" +outerCourseClassQueryVo.getSearchKeyword()+ ".*"),Criteria.where("teacherName").regex(".*" +outerCourseClassQueryVo.getSearchKeyword()+ ".*")));
        }

        if(StringUtils.isNotEmpty(outerCourseClassQueryVo.getClassTimeOfDays())){
            String classTimeOfDays = outerCourseClassQueryVo.getClassTimeOfDays();
            String[] timeItems = classTimeOfDays.split(",");
            List<Criteria> ors = new ArrayList<>(timeItems.length);
            for(String times : timeItems ){
                if(StringUtils.isEmpty(times)){
                    continue;
                }
                times = new String( Base64.decodeBase64(times));
                String[] parts = times.split("-");
                ors.add(Criteria.where("studyingTime").gte(parts[0]).lte(parts[1]));
            }

            criteria.andOperator(new Criteria().orOperator(ors.toArray(new Criteria[ors.size()])));
        }
        return criteria;
    }

    private Aggregation buildAggregation(OuterCourseClassQueryVo outerCourseClassQueryVo,int pageNo,int pageSize){

        Validate.notNull(outerCourseClassQueryVo.getAreaTeamId());

        Aggregation aggregation=null;
        Criteria criteria=this.buildCriteria(outerCourseClassQueryVo);
        if(StringUtils.isEmpty(outerCourseClassQueryVo.getCampusIds()) && outerCourseClassQueryVo.getLongitude()!=null && outerCourseClassQueryVo.getLatitude()!=null){
            Point point=new Point(outerCourseClassQueryVo.getLongitude(),outerCourseClassQueryVo.getLatitude());
            NearQuery query=NearQuery.near(point);

            query.query(new Query().addCriteria(criteria));

            query.num(500000);

            aggregation=Aggregation.newAggregation(
                     Aggregation.geoNear(query,"dis"),
                    Aggregation.sort(Sort.Direction.DESC,"isRecommend").and(Sort.Direction.ASC,"dis").and(Sort.Direction.ASC,"progressStatus").and(Sort.Direction.ASC,"startCourseTime"),

                    Aggregation.skip(pageNo*pageSize),
                    Aggregation.limit(pageSize)


            );
        }else {
            aggregation=Aggregation.newAggregation(
                    Aggregation.match(criteria),
                    Aggregation.sort(Sort.Direction.DESC,"isRecommend").and(Sort.Direction.ASC,"progressStatus").and(Sort.Direction.ASC,"startCourseTime"),
                    Aggregation.skip(pageNo*pageSize),
                    Aggregation.limit(pageSize)

            );
        }


        return aggregation;
    }


    private Query buildCourseQuery(OuterCourseClassQueryVo outerCourseClassQueryVo) {
        Query query=new Query().addCriteria(this.buildCriteria(outerCourseClassQueryVo));
      return query;

    }


    @Override
    public List<OuterCourseClass> findByClassIds(List<Long> classIds) {
        Criteria criteria = new Criteria();
        return  Lists.transform(mongoTemplate.find(new Query().addCriteria(criteria.where("classId").in(classIds)),OuterCourseClass.class),this::fixStudyingFee );
    }

    @Override
    public OuterCourseClass findByClassId(Long classId) {

        return this.fixStudyingFee( courseClassRepository.findByClassId(classId));
    }

    @Override
    public CourseClassDetail findClassDetailByClassId(Long classId) {
        Criteria criteria = new Criteria();
        CourseClassDetail courseClassDetail=mongoTemplate.findOne(new Query().addCriteria(criteria.where("classId").is(classId)),CourseClassDetail.class);
        return  (CourseClassDetail)this.fixStudyingFee(courseClassDetail);
    }



    @Override
    public SimpleCourseClass findSimpleCourseClassByClassId(Long classId) {
        Criteria criteria = new Criteria();
        Query query=new Query();
        query.addCriteria(criteria.where("classId").is(classId));
        SimpleCourseClass courseClassResultVo=mongoTemplate.findOne(query,SimpleCourseClass.class);
        return   this.fixStudyingFee( courseClassResultVo );
    }

    @Override
    public List<SimpleCourseClass> findSimpleCourseClassByClassIds(List<Long> classIds) {
        Criteria criteria = new Criteria();
        Query query=new Query();
        query.addCriteria(criteria.where("classId").in(classIds));
        List<SimpleCourseClass> simpleCourseClasses = mongoTemplate.find(query, SimpleCourseClass.class);
        return Lists.transform( simpleCourseClasses ,this::fixStudyingFee );
    }

    @Override
    public List<ScheduleInfoVo> findScheduleInfoByUserIdAndDate(Long studentId,Long userId, String  dateString) {
        if (userId==null || studentId==null){
            return Collections.EMPTY_LIST;
        }
        List<ScheduleInfoVo> scheduleInfoVoList=new ArrayList<ScheduleInfoVo>();
        Date date=Utils.parseDate(dateString,"yyyy-MM");
        Date startDate= TimeUtils.firstDateOfMonth(date);
        Date endDate= TimeUtils.changeOneDateLastTime(TimeUtils.lastDateOfMonth(date));
        AttendClassRecordDetailQueryVo attendClassRecordDetailQueryVo=new AttendClassRecordDetailQueryVo();
        attendClassRecordDetailQueryVo.setStudentId(studentId);
        attendClassRecordDetailQueryVo.setStartDate(startDate);
        attendClassRecordDetailQueryVo.setEndDate(endDate);
        List<AttendClassRecordDetail> attendClassRecordDetailList=attendClassRecordDetailService.findAttendClassRecordDetailByQueryVo(Arrays.asList(0,2),attendClassRecordDetailQueryVo);
        if (attendClassRecordDetailList==null){
            return Collections.EMPTY_LIST;
        }
        for (AttendClassRecordDetail attendClassRecordDetail:attendClassRecordDetailList){
            ScheduleInfoVo scheduleInfoVo = new ScheduleInfoVo();
            scheduleInfoVo.setStudentId(attendClassRecordDetail.getStudentId());
            scheduleInfoVo.setStudentName(attendClassRecordDetail.getStudentName());
            scheduleInfoVo.setLectureNo(attendClassRecordDetail.getLectureNo());
            scheduleInfoVo.setStudyingTime(attendClassRecordDetail.getStartTime()+"-"+attendClassRecordDetail.getEndTime());
            scheduleInfoVo.setClassDate(attendClassRecordDetail.getClassDate());

            scheduleInfoVo.setCampusId(attendClassRecordDetail.getCampusId());
            scheduleInfoVo.setCampusName(attendClassRecordDetail.getCampusName());
            scheduleInfoVo.setRoomId(attendClassRecordDetail.getRoomId());
            scheduleInfoVo.setRoomName(attendClassRecordDetail.getRoomName());
            scheduleInfoVo.setClassId(attendClassRecordDetail.getClassId());
            scheduleInfoVo.setClassName(attendClassRecordDetail.getClassName());
            scheduleInfoVoList.add(scheduleInfoVo);
        }
        return scheduleInfoVoList;
    }


}
